﻿using System.Collections.Generic;
using System.IO;

namespace DnPakExplorer {

	/// <summary>
	/// 龙之谷数据表（二维表）
	/// </summary>
	public class Dntable {
		/// <summary>
		/// 属性列
		/// </summary>
		DntField[] mFields;
		/// <summary>
		/// 记录
		/// </summary>
		DntRecord[] mRecords;
		Dictionary<string, int> mFieldIndex;
		/// <summary>
		/// 键
		/// </summary>
		int[] mKey;

		public static DntField[] LoadFields(Stream stream) {
			BinaryReader br = new BinaryReader(stream);
			br.ReadInt32();
			int fieldCount = br.ReadInt16();
			int recourdCount = br.ReadInt32();
			byte[] buff = new byte[256];
			DntField[] fields = new DntField[fieldCount];
			for (int i=0; i<fieldCount; i++) {
				fields[i] = new DntField();
				short nameLen = br.ReadInt16();
				br.Read(buff, 0, nameLen);
				fields[i].name = System.Text.Encoding.Default.GetString(buff, 0, nameLen);
				fields[i].type = (DntFieldType)br.ReadByte();
			}
			br.Close();
			return fields;
		}

		/// <summary>
		/// 通过Stream加载数据表
		/// </summary>
		public void Load(Stream stream) {
			BinaryReader br = new BinaryReader(stream);
			br.ReadInt32();
			int fieldCount = br.ReadInt16();
			int recourdCount = br.ReadInt32();
			byte[] buff = new byte[256];
			DntField[] fields = mFields = new DntField[fieldCount];
			mFieldIndex = new Dictionary<string, int>();
			for (int i=0; i<fieldCount; i++) {
				fields[i] = new DntField();
				short nameLen = br.ReadInt16();
				br.Read(buff, 0, nameLen);
				fields[i].name = System.Text.Encoding.Default.GetString(buff, 0, nameLen);
				fields[i].type = (DntFieldType)br.ReadByte();
				mFieldIndex.Add(fields[i].name, i);

				if (fields[i].type == DntFieldType.Int32 && (fields[i].name.EndsWith("SkinName")||fields[i].name.EndsWith("AniName")||fields[i].name.EndsWith("ActName"))) {
					fields[i].fileNameId = true;
				} else {
					if ((fields[i].name.EndsWith("Name") || fields[i].name.EndsWith("NameID") || fields[i].name.EndsWith("DescriptionID") || fields[i].name.EndsWith("ExplanationID")) && fields[i].type == DntFieldType.Int32) {
						fields[i].uistring = true;
					}
					if ((fields[i].name.EndsWith("NameIDParam") || fields[i].name.EndsWith("DescriptionIDParam") || fields[i].name.EndsWith("ExplanationIDParam")) && fields[i].type == DntFieldType.String) {
						fields[i].uiparams = true;
					}
				}
			}

			for (int i=0; i<fieldCount; i++) {
				if (fields[i].uistring) {
					for (int j=i+1; j<fieldCount; j++) {
						if (fields[j].uiparams && fields[j].name.IndexOf(fields[i].name)>=0) {
							fields[i].paramsIndex = j;
						}
					}
				}
			}

			DntRecord[] records = mRecords = new DntRecord[recourdCount];
			mKey = new int[recourdCount];
			for (int r=0; r<recourdCount; r++) {
				records[r] = new DntRecord(fieldCount);
				mKey[r] = br.ReadInt32();
				for (int c=0; c<fieldCount; c++) {
					switch (fields[c].type) {
					case DntFieldType.Bool32:
						records[r].value[c] = br.ReadInt32()!=0;
						break;

					case DntFieldType.Float:
						records[r].value[c] = br.ReadSingle();
						break;

					case DntFieldType.FloatPer:
						records[r].value[c] = br.ReadSingle();
						break;

					case DntFieldType.Int32:
						records[r].value[c] = br.ReadInt32();
						break;

					case DntFieldType.String:
						short stringLen = br.ReadInt16();
						if (stringLen>buff.Length)
							buff = new byte[stringLen];
						br.Read(buff, 0, stringLen);
						records[r].value[c] = System.Text.Encoding.Default.GetString(buff, 0, stringLen);
						break;

					}
				}

			}

			br.Close();
		}

		/// <summary>
		/// 通过文件(*.Dnt)加载数据表
		/// </summary>
		/// <param name="filename">文件名</param>
		public void Load(string filename) {
			using (FileStream file = new FileStream(filename, FileMode.Open)) {
				Load(file);
			}
		}

		/// <summary>
		/// 表的所有属性列
		/// </summary>
		/// <returns></returns>
		public DntField[] GetFields() {
			return mFields;
		}

		/// <summary>
		/// 索引或者设置指定行列出的表项
		/// </summary>
		/// <param name="r">表行</param>
		/// <param name="c">表列</param>
		public object this[int r, int c] {
			get { return mRecords[r].value[c]; }
			set { mRecords[r].value[c] = value; }
		}

		/// <summary>
		/// 索引或者设置指定行列出的表项
		/// </summary>
		/// <param name="r">表行</param>
		/// <param name="c">表列名称</param>
		public object this[int r, string c] {
			get { return mRecords[r].value[mFieldIndex[c]]; }
			set { mRecords[r].value[mFieldIndex[c]] = value; }
		}

		/// <summary>
		/// 获取属性列
		/// </summary>
		/// <param name="index">列索引</param>
		public DntField GetField(int index) {
			return mFields[index];
		}

		/// <summary>
		/// 将多个有着相同属性列的表联合成一个，如果存在两个表的对应属性不同，则会抛出异常
		/// </summary>
		/// <param name="tables">要联合的表集合</param>
		/// <returns>联合后的结果表</returns>
		public static Dntable UnionWith(List<Dntable> tables) {
			if (tables.Count == 0)
				return null;
			Dntable table = tables[0];
			int totalCount = 0;
			foreach (Dntable t in tables) {
				if (t.FieldCount == table.FieldCount) {
					for (int i=0; i<table.mFields.Length; i++) {
						if (t.mFields[i].type != table.mFields[i].type) {
							throw new System.ArgumentException("属性不同的表不能进行联合操作。");
						}
					}
				} else {
					throw new System.ArgumentException("属性不同的表不能进行联合操作。");
				}
				totalCount += t.RecordCount;
			}

			Dntable newTable = new Dntable();
			newTable.mFields = table.mFields;
			newTable.mFieldIndex = table.mFieldIndex;
			newTable.mRecords = new DntRecord[totalCount];
			newTable.mKey = new int[totalCount];

			int index = 0;
			foreach (Dntable t in tables) {
				System.Array.Copy(t.mRecords, 0, newTable.mRecords, index, t.RecordCount);
				System.Array.Copy(t.mKey, 0, newTable.mKey, index, t.RecordCount);
				index += t.RecordCount;
			}
			return newTable;
		}

		/// <summary>
		/// 属性列个数
		/// </summary>
		public int FieldCount {
			get { return mFields.Length; }
		}

		/// <summary>
		/// 记录（行）数
		/// </summary>
		public int RecordCount {
			get { return mRecords.Length; }
		}

		/// <summary>
		/// 获取index行处的简直
		/// </summary>
		public int GetKey(int index) {
			return mKey[index];
		}

		/// <summary>
		/// 清空数据表
		/// </summary>
		public void Clear() {
			mFields = null;
			mRecords = null;
		}

		/// <summary>
		/// 查找指定键值的索引（行）
		/// </summary>
		/// <returns>未找到返回-1</returns>
		public int IndexOfKey(int key) {
			for (int i=0; i< mKey.Length; i++) {
				if (key == mKey[i])
					return i;
			}
			return -1;
		}

		public int IndexOfField(int fieldIndex, object value){
			switch(mFields[fieldIndex].type) {
			case DntFieldType.Int32:
				int intValue = (int)value;
				for (int i=0; i<mRecords.Length; i++) {
					if ((int)mRecords[i].value[fieldIndex] == intValue) {
						return i;
					}
				}
				break;
			case DntFieldType.Float:
			case DntFieldType.FloatPer:
				float floatValue = (float)value;
				for (int i=0; i<mRecords.Length; i++) {
					if ((float)mRecords[i].value[fieldIndex] == floatValue) {
						return i;
					}
				}
				break;
			case DntFieldType.Bool32:
				bool boolValue = (bool)value;
				for (int i=0; i<mRecords.Length; i++) {
					if ((bool)mRecords[i].value[fieldIndex] == boolValue) {
						return i;
					}
				}
				break;
			case DntFieldType.String:
				string stringValue = (string)value;
				for (int i=0; i<mRecords.Length; i++) {
					if ((string)mRecords[i].value[fieldIndex] == stringValue) {
						return i;
					}
				}
				break;
			}
			return -1;
		}
	}

	/// <summary>
	/// 表示属性列的类
	/// </summary>
	public class DntField {
		/// <summary>
		/// 属性列名
		/// </summary>
		public string name;
		/// <summary>
		/// 属性列类型
		/// </summary>
		public DntFieldType type;

		/// <summary>
		/// 表示的内容是否是UIString
		/// </summary>
		public bool uistring = false;
		/// <summary>
		/// UIString是否含有UIParams
		/// </summary>
		public bool uiparams = false;
		public bool fileNameId = false;
		public int paramsIndex = -1;
		public override string ToString() {
			return name;
		}
	}

	/// <summary>
	/// 表示数据表记录的类
	/// </summary>
	public class DntRecord {
		public object[] value;
		public DntRecord(int size) {
			value = new object[size];
		}
	}

	/// <summary>
	/// 属性列类型
	/// </summary>
	public enum DntFieldType {
		String		=	1,		//1：字段类型为普通变长文本
		Bool32		=	2,		//2：字段类型为布尔型（以32位整型存放）
		Int32		=	3,		//3：字段类型为32位整型
		FloatPer	=	4,		//4：字段类型百分比（该字段为单精度浮点型，但要除以100）
		Float		=	5,		//5：字段类型单精度浮点型

	}
}
